const WAITING = 'waiting'
const RESOLVE = 'resolve'
const REJECT = 'reject'
/**
 * 自定义 promise 的实现
 * 1. promsie 存在的状态 waiting(等待)，resolve(解决)，reject(拒绝)
 * 2. 通过 then 方法收集回调，并进行链式调用
 * 3. 状态为等待时调用 then 函数则通过回调收集依赖。不过不是等待这直接通过微任务调用
 * */ 
class MyPromise{
  constructor(callback) {
    callback(this.resolve, this.reject)
  }

  // 状态
  state = WAITING
  // 成功时的回调列表
  resolveCallbackList = []
  // 拒绝时的回调列表
  rejectCallbackList = []
  // resolve 时接收的值
  resolveVal
  // reject 时接收的值
  rejectVal

  // 解决
  resolve = (val) => {
    // 状态为 waiting（等待） 时则不执行
    if (this.state !== WAITING) return
    
    this.resolveVal = val
    this.state = RESOLVE
    // 异步执行时调用回调
    this.resolveCallbackList.forEach((callback) => {
      callback(val)
    })
  }

  // 拒绝
  reject = (val) => {
    // 状态为 waiting（等待） 时则不执行
    if (this.state !== WAITING) return

    this.rejectVal = val
    this.state = REJECT
    // 异步执行时调用回调
    this.rejectCallbackList.forEach((callback) => {
      callback(val)
    })
  }

  /**
   * 收集回调
   * @param {function?} resolveCb 解决时的回调
   * @param {function?} rejectCb 拒绝时的回调
   * */ 
  then(resolveCb, rejectCb) {
    // 返回一个 myPromise 实例来实现链式调用
    return new MyPromise((childResolve, childReject) => {
      // 封装回调,实现链式调用的回调调用
      // 上一个回调如果返回的是 promise 则需要等待返回的 promsie 执行完毕后在继续执行
      const resolveCallback = (val) => {
        // 只是为了尝试实现，所以这里使用任意的可以启动微任务的 api 即可
        process.nextTick(() => {
          const resVal = typeof resolveCb === 'function' ? resolveCb(val) : val

          // 如果回调返回了一个 MyPromise 实例，则等待它执行完毕在进行下一步的链式调用
          if (resVal instanceof MyPromise) {
            resVal
              .then(childResolve, childReject)
            return
          }
          childResolve(resVal)
        })
      }
      // 错误回调不需要链式调用，只需要向下传递，直到发现一个错误处理回调即可
      const rejectCallback = (val) => {
        process.nextTick(() => {
          if (typeof rejectCb === 'function') {
            rejectCb(val)
          } else {
            childReject(val)
          }
        })
      }
      
      // 处理回调
      if (this.state === WAITING) {
        // 1. 如果状态为等待，则将相应的回调存起来。
        this.resolveCallbackList.push(resolveCallback)
        this.rejectCallbackList.push(rejectCallback)
      } else if (this.state === RESOLVE) {
        // 如果状态已经改变（非等待），则立即调用对应的回调
        resolveCallback(this.resolveVal)
      } else if (this.state === REJECT) {
        rejectCallback(this.rejectVal)
      }
    })
  }

  // 捕获一个异常
  catch(rejectCb) {
    return this.then(val => val, rejectCb)
  }
}

const promise = new MyPromise((resolve, reject) => {
  // resolve('res')
  reject('发生了错误')
  // setTimeout(() => {
  // }, 10)
})
console.log('开始')
promise
  .then((res) => {
    console.log('res', res)
    return 'then1'
  })
  .then((res) => {
    console.log(res)
    return new MyPromise((resolve,reject) => {
      setTimeout(() => {resolve('这是中间插入的一个 promise')}, 2000)
    })
  })
  .catch((err) => {
    console.log(`catch错误信息： ${err}`)
  })
  .then((res) => {
    console.log(res)
  }, (err) => {
    console.log(`错误信息： ${err}`)
  })
console.log('结束')

promise
  .then((res) => {
    console.log('res1', res)
  })
  .catch((err) => {
    console.log(`异常1：${err}`)
  })

if (process.argv.indexOf('debug') !== -1) {
  // 原生 promise 的调试
  const promise = new Promise((resolve, reject) => {
    console.log('resolve')
    setTimeout(() => resolve('立即执行了 resolve'), 20)
  })

  console.log('end')

  promise
    .then((res) => {
      console.log(res)
    }, (err) => {
      console.log(`发生错误: ${err}`)
    })
    .catch((err) => {
      console.log(`catch报的错误: ${err}`)
    })
    .then(() => {
      console.log('下一级的回调')
    })

  setTimeout(() => {
    console.log('10毫秒的计时器')
    promise.then((res) => {
      console.log('setTimeout 里promise的回调')
    }, (err) => {
      console.log(`发生错误10: ${err}`)
    })
  }, 10)

  setTimeout(() => {
    console.log('30毫秒的计时器')
    promise.then((res) => {
      console.log('setTimeout 里promise的回调')
    }, (err) => {
      console.log(`发生错误30: ${err}`)
    })
    .then(() => {
      console.log('下一级的回调30')
    })
  }, 30)
}
